package com.example.apitesttool.service;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.info.Info;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * OpenAPI 文档管理器
 * 
 * 负责管理已上传的 OpenAPI/Swagger 文档，提供文档的存储、查询、删除等功能。
 * 使用内存存储，支持多个文档同时管理，并提供当前活跃文档的概念。
 * 
 * 特性：
 * - 内存存储多个 OpenAPI 文档
 * - 自动生成唯一文档 ID
 * - 支持设置当前活跃文档
 * - 提供文档信息查询和统计
 * - 线程安全的文档操作
 * 
 * @author liuxy
 * @version 1.0.0
 */
@Service
public class OpenApiDocumentManager {
    
    private static final Logger logger = LoggerFactory.getLogger(OpenApiDocumentManager.class);
    
    // 存储 OpenAPI 文档的映射，Key 是文档 ID，Value 是文档对象
    private final Map<String, OpenAPI> documents = new ConcurrentHashMap<>();
    
    // 存储文档元数据的映射
    private final Map<String, DocumentMetadata> documentMetadata = new ConcurrentHashMap<>();
    
    // 当前活跃的文档 ID
    private volatile String currentActiveDocumentId = null;

    /**
     * 文档元数据内部类
     */
    public static class DocumentMetadata {
        private final String id;
        private final String title;
        private final String version;
        private final String description;
        private final LocalDateTime uploadTime;
        private final int pathCount;
        private final int operationCount;

        public DocumentMetadata(String id, String title, String version, String description, 
                              LocalDateTime uploadTime, int pathCount, int operationCount) {
            this.id = id;
            this.title = title;
            this.version = version;
            this.description = description;
            this.uploadTime = uploadTime;
            this.pathCount = pathCount;
            this.operationCount = operationCount;
        }

        // Getters
        public String getId() { return id; }
        public String getTitle() { return title; }
        public String getVersion() { return version; }
        public String getDescription() { return description; }
        public LocalDateTime getUploadTime() { return uploadTime; }
        public int getPathCount() { return pathCount; }
        public int getOperationCount() { return operationCount; }
    }

    /**
     * 存储一个 OpenAPI 文档
     * 
     * @param openAPI OpenAPI 文档对象
     * @return 生成的文档 ID
     */
    public String storeDocument(OpenAPI openAPI) {
        if (openAPI == null) {
            throw new IllegalArgumentException("OpenAPI 文档不能为空");
        }

        String documentId = generateDocumentId(openAPI);
        
        // 计算文档统计信息
        int pathCount = openAPI.getPaths() != null ? openAPI.getPaths().size() : 0;
        int operationCount = calculateOperationCount(openAPI);
        
        // 创建文档元数据
        Info info = openAPI.getInfo();
        DocumentMetadata metadata = new DocumentMetadata(
            documentId,
            info != null ? info.getTitle() : "未知标题",
            info != null ? info.getVersion() : "未知版本",
            info != null ? info.getDescription() : "无描述",
            LocalDateTime.now(),
            pathCount,
            operationCount
        );

        // 存储文档和元数据
        documents.put(documentId, openAPI);
        documentMetadata.put(documentId, metadata);
        
        // 如果这是第一个文档，自动设置为活跃文档
        if (currentActiveDocumentId == null) {
            currentActiveDocumentId = documentId;
            logger.info("自动设置第一个文档为活跃文档: {}", documentId);
        }

        logger.info("存储 OpenAPI 文档成功: {} (标题: {}, 版本: {}, {} 个路径, {} 个操作)", 
                   documentId, metadata.getTitle(), metadata.getVersion(), pathCount, operationCount);
        
        return documentId;
    }

    /**
     * 获取指定 ID 的 OpenAPI 文档
     * 
     * @param documentId 文档 ID
     * @return OpenAPI 文档对象的 Optional
     */
    public Optional<OpenAPI> getDocument(String documentId) {
        if (documentId == null || documentId.trim().isEmpty()) {
            return Optional.empty();
        }
        return Optional.ofNullable(documents.get(documentId.trim()));
    }

    /**
     * 获取当前活跃的 OpenAPI 文档
     * 
     * @return 当前活跃文档的 Optional
     */
    public Optional<OpenAPI> getCurrentActiveDocument() {
        if (currentActiveDocumentId == null) {
            return Optional.empty();
        }
        return getDocument(currentActiveDocumentId);
    }

    /**
     * 设置当前活跃的文档 ID
     * 
     * @param documentId 文档 ID
     * @return 如果设置成功返回 true，否则返回 false
     */
    public boolean setCurrentActiveDocument(String documentId) {
        if (documentId == null || documentId.trim().isEmpty()) {
            logger.warn("尝试设置空的活跃文档 ID");
            return false;
        }
        
        String trimmedId = documentId.trim();
        if (!documents.containsKey(trimmedId)) {
            logger.warn("尝试设置不存在的文档为活跃文档: {}", trimmedId);
            return false;
        }
        
        String oldActiveId = currentActiveDocumentId;
        currentActiveDocumentId = trimmedId;
        logger.info("活跃文档已从 {} 切换到 {}", oldActiveId, trimmedId);
        return true;
    }

    /**
     * 获取当前活跃文档的 ID
     * 
     * @return 当前活跃文档 ID 的 Optional
     */
    public Optional<String> getCurrentActiveDocumentId() {
        return Optional.ofNullable(currentActiveDocumentId);
    }

    /**
     * 删除指定 ID 的文档
     * 
     * @param documentId 文档 ID
     * @return 如果删除成功返回 true，否则返回 false
     */
    public boolean removeDocument(String documentId) {
        if (documentId == null || documentId.trim().isEmpty()) {
            return false;
        }
        
        String trimmedId = documentId.trim();
        OpenAPI removedDoc = documents.remove(trimmedId);
        DocumentMetadata removedMetadata = documentMetadata.remove(trimmedId);
        
        if (removedDoc != null) {
            // 如果删除的是当前活跃文档，需要重新设置活跃文档
            if (trimmedId.equals(currentActiveDocumentId)) {
                currentActiveDocumentId = documents.isEmpty() ? null : documents.keySet().iterator().next();
                logger.info("删除了活跃文档，新的活跃文档: {}", currentActiveDocumentId);
            }
            
            logger.info("删除 OpenAPI 文档: {} (标题: {})", trimmedId, 
                       removedMetadata != null ? removedMetadata.getTitle() : "未知");
            return true;
        }
        
        return false;
    }

    /**
     * 清除所有文档
     */
    public void clearAllDocuments() {
        int count = documents.size();
        documents.clear();
        documentMetadata.clear();
        currentActiveDocumentId = null;
        logger.info("清除了所有 {} 个 OpenAPI 文档", count);
    }

    /**
     * 检查指定 ID 的文档是否存在
     * 
     * @param documentId 文档 ID
     * @return 如果文档存在返回 true
     */
    public boolean hasDocument(String documentId) {
        if (documentId == null || documentId.trim().isEmpty()) {
            return false;
        }
        return documents.containsKey(documentId.trim());
    }

    /**
     * 获取所有文档的 ID 列表
     * 
     * @return 文档 ID 列表
     */
    public List<String> getAllDocumentIds() {
        return new ArrayList<>(documents.keySet());
    }

    /**
     * 获取所有文档的元数据
     * 
     * @return 文档元数据列表
     */
    public List<DocumentMetadata> getAllDocumentMetadata() {
        return new ArrayList<>(documentMetadata.values());
    }

    /**
     * 获取指定文档的元数据
     * 
     * @param documentId 文档 ID
     * @return 文档元数据的 Optional
     */
    public Optional<DocumentMetadata> getDocumentMetadata(String documentId) {
        if (documentId == null || documentId.trim().isEmpty()) {
            return Optional.empty();
        }
        return Optional.ofNullable(documentMetadata.get(documentId.trim()));
    }

    /**
     * 获取文档总数
     * 
     * @return 文档总数
     */
    public int getDocumentCount() {
        return documents.size();
    }

    /**
     * 获取所有文档的格式化字符串表示
     * 
     * @return 格式化的文档列表字符串
     */
    public String getDocumentsAsString() {
        if (documents.isEmpty()) {
            return "当前没有已上传的 OpenAPI 文档";
        }
        
        StringBuilder sb = new StringBuilder();
        sb.append("已上传的 OpenAPI 文档 (").append(documents.size()).append(" 个):\n");
        
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        
        documentMetadata.values().forEach(metadata -> {
            sb.append("  ID: ").append(metadata.getId());
            if (metadata.getId().equals(currentActiveDocumentId)) {
                sb.append(" [当前活跃]");
            }
            sb.append("\n");
            sb.append("    标题: ").append(metadata.getTitle()).append("\n");
            sb.append("    版本: ").append(metadata.getVersion()).append("\n");
            sb.append("    描述: ").append(metadata.getDescription()).append("\n");
            sb.append("    上传时间: ").append(metadata.getUploadTime().format(formatter)).append("\n");
            sb.append("    接口数量: ").append(metadata.getPathCount()).append(" 个路径, ")
              .append(metadata.getOperationCount()).append(" 个操作\n");
            sb.append("\n");
        });
        
        return sb.toString();
    }

    /**
     * 生成文档 ID
     * 
     * @param openAPI OpenAPI 文档对象
     * @return 生成的文档 ID
     */
    private String generateDocumentId(OpenAPI openAPI) {
        String baseId;
        
        // 尝试使用文档标题作为基础 ID
        if (openAPI.getInfo() != null && openAPI.getInfo().getTitle() != null && 
            !openAPI.getInfo().getTitle().trim().isEmpty()) {
            baseId = openAPI.getInfo().getTitle()
                    .replaceAll("[^a-zA-Z0-9_.-]", "_")  // 替换特殊字符
                    .replaceAll("_{2,}", "_")            // 合并多个下划线
                    .toLowerCase();
        } else {
            baseId = "openapi_doc";
        }
        
        // 确保 ID 唯一性
        String candidateId = baseId;
        int counter = 1;
        while (documents.containsKey(candidateId)) {
            candidateId = baseId + "_" + counter;
            counter++;
        }
        
        return candidateId;
    }

    /**
     * 计算 OpenAPI 文档中的操作总数
     * 
     * @param openAPI OpenAPI 文档对象
     * @return 操作总数
     */
    private int calculateOperationCount(OpenAPI openAPI) {
        if (openAPI.getPaths() == null) {
            return 0;
        }
        
        int count = 0;
        for (PathItem pathItem : openAPI.getPaths().values()) {
            if (pathItem.getGet() != null) count++;
            if (pathItem.getPost() != null) count++;
            if (pathItem.getPut() != null) count++;
            if (pathItem.getDelete() != null) count++;
            if (pathItem.getPatch() != null) count++;
            if (pathItem.getHead() != null) count++;
            if (pathItem.getOptions() != null) count++;
            if (pathItem.getTrace() != null) count++;
        }
        
        return count;
    }

    /**
     * 根据路径和 HTTP 方法查找操作
     * 
     * @param documentId 文档 ID
     * @param path API 路径
     * @param httpMethod HTTP 方法
     * @return 操作对象的 Optional
     */
    public Optional<Operation> findOperation(String documentId, String path, String httpMethod) {
        Optional<OpenAPI> docOpt = getDocument(documentId);
        if (!docOpt.isPresent()) {
            return Optional.empty();
        }
        
        OpenAPI openAPI = docOpt.get();
        if (openAPI.getPaths() == null) {
            return Optional.empty();
        }
        
        PathItem pathItem = openAPI.getPaths().get(path);
        if (pathItem == null) {
            return Optional.empty();
        }
        
        switch (httpMethod.toUpperCase()) {
            case "GET": return Optional.ofNullable(pathItem.getGet());
            case "POST": return Optional.ofNullable(pathItem.getPost());
            case "PUT": return Optional.ofNullable(pathItem.getPut());
            case "DELETE": return Optional.ofNullable(pathItem.getDelete());
            case "PATCH": return Optional.ofNullable(pathItem.getPatch());
            case "HEAD": return Optional.ofNullable(pathItem.getHead());
            case "OPTIONS": return Optional.ofNullable(pathItem.getOptions());
            case "TRACE": return Optional.ofNullable(pathItem.getTrace());
            default: return Optional.empty();
        }
    }
}

